package com.hmdp.service.impl;

import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.hmdp.dto.Result;
import com.hmdp.entity.Shop;
import com.hmdp.mapper.ShopMapper;
import com.hmdp.service.IShopService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {

    @Autowired
    public StringRedisTemplate stringRedisTemplate;

    /**
     * 根据id查询商铺数据
     *
     * @param id
     * @return
     */
        @Override
    public Result queryById(Long id) {
            //缓存穿透
            //Shop shop = queryWithPassThrough(id);

           //互斥锁解决缓存穿透
           Shop shop=queryWithMutex(id);
           if(shop ==null){
               return Result.fail("店铺不存在");
           }
           return Result.ok(shop);
        }


    public Shop queryWithMutex(Long id){
        String key = "cache:shop:" + id;
        // 1、从Redis中查询店铺数据
        String shopJson = stringRedisTemplate.opsForValue().get(key);
        //( shopJson有三种情况：1. 成功缓存的数据,  2. null(查不出来) ,  3. ""(查出为解决缓存穿透而存的"")  )

        //2.判断是否存在
        if (StrUtil.isNotBlank(shopJson)) {
            // 3.缓存命中，直接返回店铺数据
            Shop shop = JSONUtil.toBean(shopJson, Shop.class);
            return shop;
        }
        //判断命中的是否是""
        if(shopJson !=null){
            return null;
        }
        //获得互斥锁
        String LocKey="lock:shop"+id;
        Shop shop= null;
        try {
            boolean isLock = tryLock(LocKey);
            //判断是否成功
            if(!isLock){
                //失败，休眠
                Thread.sleep(50);
                 return queryWithMutex(id);//递归循环执行
            }

            //成功，下一步
            //4.不存在，查数据库
            shop = getById(id);
            Thread.sleep(200);
            //5.不存在，返回erro
            if(shop==null){
                //将空值("")写入Redis
                stringRedisTemplate.opsForValue().set(key,"",3L, TimeUnit.MINUTES);
                return null;
            }
            //6.存在。存入redis
            stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop),30L, TimeUnit.MINUTES);//shop转JSON,有效期30分钟
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }finally {
            //释放互斥锁
            unlock(LocKey);
        }

        //7.返回
        return shop;

    }
    public Shop queryWithPassThrough(Long id){
        String key = "cache:shop:" + id;
        // 1、从Redis中查询店铺数据
        String shopJson = stringRedisTemplate.opsForValue().get(key);
        //( shopJson有三种情况：1. 成功缓存的数据,  2. null(查不出来) ,  3. ""(查出为解决缓存穿透而存的"")  )

        //2.判断是否存在
        if (StrUtil.isNotBlank(shopJson)) {
            // 3.缓存命中，直接返回店铺数据
            Shop shop = JSONUtil.toBean(shopJson, Shop.class);
            return shop;
        }
        //判断命中的是否是""
        if(shopJson !=null){
            return null;
        }
        //4.不存在，查数据库
        Shop shop=getById(id);

        //5.不存在，返回erro
        if(shop==null){
            //将空值("")写入Redis
            stringRedisTemplate.opsForValue().set(key,"",3L, TimeUnit.MINUTES);
            return null;
        }
        //6.存在。存入redis
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop),30L, TimeUnit.MINUTES);//shop转JSON,有效期30分钟
        //7.返回
        return shop;
    }



    /**
     * 获取所
     * @param key
     * @return
     */
    private boolean tryLock(String key){
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);//过期时间10秒钟
        return BooleanUtil.isTrue(flag);
    }

    /**
     * 释放锁
     * @param key
     */
    private void unlock(String key){
        stringRedisTemplate.delete(key);
    }



    /**
     * 更新商铺信息
     * @param shop 商铺数据
     * @return 无
     */
    @Override
    @Transactional  //事务回滚
    public Result update(Shop shop) {
        Long id =shop.getId();
        if(id==null){
            return Result.fail("店铺ID不能为空");
        }
        //1.先更新数据库
        updateById(shop);

        //2.再删除缓存
        String key = "cache:shop:" + id;
        stringRedisTemplate.delete(key);
        return Result.ok();
    }
}





















//    @Override
//    public Result queryById(Long id) {
//        String key = "cache:shop:" + id;
//        // 1、从Redis中查询店铺数据
//        String shopJson = stringRedisTemplate.opsForValue().get(key);
//        //( shopJson有三种情况：1. 成功缓存的数据,  2. null(查不出来) ,  3. ""(查出为解决缓存穿透而存的"")  )
//        //2.判断是否存在
//        if (StrUtil.isNotBlank(shopJson)) {
//            // 3.缓存命中，直接返回店铺数据
//           Shop shop = JSONUtil.toBean(shopJson, Shop.class);
//            return Result.ok(shop);
//        }
//        //判断命中的是否是""
//        if(shopJson !=null){
//            return Result.fail("店铺信息不存在");
//        }
//        //4.不存在，查数据库
//        Shop shop=getById(id);
//        //5.不存在，返回erro
//        if(shop==null){
//            //将空值("")写入Redis
//            stringRedisTemplate.opsForValue().set(key,"",3L, TimeUnit.MINUTES);
//            return Result.fail("店铺不存在");
//        }
//        //6.存在。存入redis
//        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop),30L, TimeUnit.MINUTES);//shop转JSON,有效期30分钟
//        //7.返回
//        return Result.ok(shop);
//
//    }
